package cn.micro.request.log.aop;

import cn.micro.core.context.CurrentContext;
import cn.micro.core.util.JsonUtil;
import cn.micro.core.util.StringUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

import static cn.micro.request.log.constant.Cants.X_CURRENT_REQUEST_LOGGING;

@Aspect
@Order(-2)
public class RequestAspect {

    private static final Map<String, Logger> loggerMap = new ConcurrentHashMap<>();

    @Pointcut("@annotation(org.springframework.web.bind.annotation.GetMapping) " + "&& !@annotation(IgnoreLogging)")
    public void getMappingPoint() {
    }

    @Pointcut("@annotation(org.springframework.web.bind.annotation.PostMapping) " + "&& !@annotation(IgnoreLogging)")
    public void postMappingPoint() {
    }

    @Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping) " + "&& !execution(* org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.*(..)) " + "&& !@annotation(IgnoreLogging)")
    public void requestMappingPoint() {
    }

    @Around("getMappingPoint() || postMappingPoint() || requestMappingPoint()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        boolean isLog = CurrentContext.get(X_CURRENT_REQUEST_LOGGING, false);
        Class<?> target = point.getTarget().getClass();
        MethodSignature methodSignature = (MethodSignature) point.getSignature();
        String loggerName = target.getName() + "." + methodSignature.getName();
        Logger logger = loggerMap.computeIfAbsent(loggerName, LoggerFactory::getLogger);
        long startTime = System.nanoTime();
        if (isLog) {
            logger.info("------ > Input Args: " + ((null == point.getArgs()) ? "null" : Arrays.stream(point.getArgs()).filter(o -> !(o instanceof HttpServletRequest || o instanceof HttpServletResponse || o instanceof MultipartFile || o instanceof MultipartFile[])).map(JsonUtil::toJson).collect(Collectors.joining(" | "))));
        }
        Object result = point.proceed();
        if (isLog) {
            logger.info("< ------ Output Cost" + ": " + StringUtil.format((System.nanoTime() - startTime)));
        }
        return result;
    }

}
